package parser;

import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;

import global.Constant;
import global.Methods;
import global.Time;

/**@author A0105661M*/
public class NewDateAndTimeAnalyzer {

	private static final int TIME_INFO_LENGTH = 2;
	private static final int HOUR = 0;
	private static final int MINUTE = 1;

	private static final int NON_EXISTENT = -1;

	private static final int DATE_INFO_LENGTH = 3;
	private static final int YEAR_IN_LONG_FORMAT = 0;
	private static final int MONTH_IN_LONG_FORMAT = 1;
	private static final int DAY_IN_LONG_FORMAT = 2;

	private static final int MONTH_IN_SHORT_FORMAT_SINGAPORE = 1;
	private static final int DAY_IN_SHORT_FORMAT_SINGAPORE = 0;
	private static final int YEAR_IN_LONG_FORMAT_SINGAPORE = 2;
	private static final int MONTH_IN_LONG_FORMAT_SINGAPORE = 1;
	private static final int DAY_IN_LONG_FORMAT_SINGAPORE = 0;

	private static final int CASUAL_TYPE = 0;
	private static final int DETAIL = 1;
	private static final int MON_TO_SUN = 0;
	private static final int MON = 1;
	private static final int TUE = 2;
	private static final int WEDN = 3;
	private static final int THUR = 4;
	private static final int FRI = 5;
	private static final int SAT = 6;
	private static final int SUN = 7;
	private static final int OFFSET = 1;
	private static final int YESTERDAY = -1;
	private static final int TODAY = 0;
	private static final int TOMORROW = 1;

	private static final int TARGET_IS_EARLIER = 1;
	private static final int BOTH_ARE_THE_SAME = 0;
	private static final int TARGET_IS_LATER = -1;
	private static final int PERIOD_OF_A_WEEK = 7;
	private static final int ZERO = 0;
	private static final int ONE = 1;
	private static final int ONE_DAY = 1;
	private static final int START_OF_ARRAY = 0;

	private static final String PERIOD_SEPARATOR = "-";

	private static final int START_TIME_HOUR = 0;
	private static final int START_TIME_MINUTE = 1;
	private static final int END_TIME_HOUR = 2;
	private static final int END_TIME_MINUTE = 3;
	private static final int START_TIME_POSITION = 6;
	private static final int END_TIME_POSITION = 7;

	private static final int TIME_AMPM_MARK = 2;
	private static final int END_TIME_HOUR_RAW = 3;

	private static final int END_TIME_AMPM_MARK = 5;

	private static final int START_DATE_YEAR = 0;
	private static final int START_DATE_MONTH = 1;
	private static final int START_DATE_DAY = 2;
	private static final int END_DATE_YEAR = 3;
	private static final int END_DATE_MONTH = 4;
	private static final int END_DATE_DAY = 5;
	private static final int START_DATE_POSITION = 6;
	private static final int END_DATE_POSITION = 7;

	public static int[] analyzeTimeInfo(String[] timingInfo, int endingIndex,
			String[] feedback, boolean[] searchOption, int[] dateInfo) {

		int[] timeInfo = new int[8];
		initializeInfoArray(timeInfo);

		for (int i = 0; i < endingIndex; i++) {
			String targetWord = timingInfo[i];
			analyzeTimeInfoInAWord(targetWord, timeInfo, feedback, i);
		}

		timeInfo = adjustTime(timeInfo, searchOption, dateInfo);
		return timeInfo;
	}

	public static int[] analyzeDateInfo(String[] timingInfo, int endingIndex,
			String[] feedback, boolean[] searchOption) {
		int[] dateInfo = new int[8];
		initializeInfoArray(dateInfo);

		for (int i = 0; i < endingIndex; i++) {
			String targetWord = timingInfo[i];
			analyzeDateInfoInAWord(targetWord, dateInfo, feedback, i);
		}

		if (searchOption[Constant.SEARCHOPTION_IS_SEARCH] != true) {
			dateInfo = adjustDate(dateInfo);
		} else {
			dateInfo = adjustDateForSearch(dateInfo, searchOption);
		}

		return dateInfo;
	}

	private static void analyzeTimeInfoInAWord(String targetWord,
			int[] timeInfo, String[] feedback, int index) {

		String[] curTarget = targetWord.trim().split(PERIOD_SEPARATOR);
		int[] curInfo;
		int[] additionalInfo;

		if (curTarget.length == 1) {
			curInfo = analyzeTime(curTarget[0], false);
			if (curInfo != null) {
				addOneTimeInfo(curInfo, timeInfo, feedback, index);
			}

		} else if (curTarget.length >= 2) {

			curInfo = analyzeTime(curTarget[0], true);
			additionalInfo = analyzeTime(curTarget[1], true);

			if (curInfo != null && additionalInfo != null) {
				if (timeInfo[START_TIME_HOUR] == NON_EXISTENT
						&& timeInfo[END_TIME_HOUR] == NON_EXISTENT) {
					Methods.transerIntArray(curInfo, 0, timeInfo, 0, 3);
					Methods.transerIntArray(additionalInfo, 0, timeInfo, 3, 3);
					timeInfo[START_TIME_POSITION] = index;
					timeInfo[END_TIME_POSITION] = index;
				}

			} else if (curInfo != null || additionalInfo != null) {
				int[] usefulInfo;

				if (curInfo != null) {
					usefulInfo = curInfo;
				} else {
					usefulInfo = additionalInfo;
				}

				addOneTimeInfo(usefulInfo, timeInfo, feedback, index);
			}
		}
	}

	private static void analyzeDateInfoInAWord(String targetWord,
			int[] dateInfo, String[] feedback, int index) {

		String[] curTarget = targetWord.trim().split(PERIOD_SEPARATOR);
		int[] curInfo;
		int[] additionalInfo;

		if (curTarget.length == 1) {
			curInfo = analyzeDate(curTarget[0]);
			if (curInfo != null) {
				addOneDateInfo(curInfo, dateInfo, feedback, index);
			}

		} else if (curTarget.length >= 2) {

			curInfo = analyzeDate(curTarget[0]);
			additionalInfo = analyzeDate(curTarget[1]);

			if (curInfo != null && additionalInfo != null) {
				if (dateInfo[START_DATE_YEAR] == NON_EXISTENT
						&& dateInfo[END_DATE_YEAR] == NON_EXISTENT) {
					Methods.transerIntArray(curInfo, 0, dateInfo, 0, 3);
					Methods.transerIntArray(additionalInfo, 0, dateInfo, 3, 3);
					dateInfo[START_DATE_POSITION] = index;
					dateInfo[END_DATE_POSITION] = index;
				}

			} else if (curInfo != null || additionalInfo != null) {
				int[] usefulInfo;

				if (curInfo != null) {
					usefulInfo = curInfo;
				} else {
					usefulInfo = additionalInfo;
				}

				addOneDateInfo(usefulInfo, dateInfo, feedback, index);
			}
		}
	}

	public static int decideTaskType(String[] timingInfo, int endingIndex,
			String[] feedback, int[] timeInfo, int[] dateInfo) {

		if (timeInfo[START_TIME_HOUR] == NON_EXISTENT
				&& dateInfo[START_DATE_YEAR] == NON_EXISTENT) {
			return Constant.FLOAT_TASK_TYPE_INT;

		} else if (timeInfo[START_TIME_HOUR] != NON_EXISTENT
				&& dateInfo[START_DATE_YEAR] == NON_EXISTENT) {
			Time currentTime = Time.currentTime();
			int[] newDate = getNextAvailableDay(currentTime, timeInfo, 1);
			Methods.transerIntArray(newDate, 0, dateInfo, 0, 3);
			Methods.transerIntArray(newDate, 0, dateInfo, 3, 3);

		} else if (timeInfo[START_TIME_HOUR] == NON_EXISTENT
				&& dateInfo[START_DATE_YEAR] != NON_EXISTENT) {
			timeInfo[START_TIME_HOUR] = 0;
			timeInfo[START_TIME_MINUTE] = 0;
			timeInfo[END_TIME_HOUR] = 23;
			timeInfo[END_TIME_MINUTE] = 59;
		}

		boolean isDeadline = false;
		for (int i = 0; i < endingIndex; i++) {

			String curTarget = timingInfo[i].toLowerCase();

			if (isDeadlineKeyword(curTarget)) {
				if (dateInfo[START_DATE_YEAR] == dateInfo[END_DATE_YEAR]
						&& dateInfo[START_DATE_MONTH] == dateInfo[END_DATE_MONTH]
						&& dateInfo[START_DATE_DAY] == dateInfo[END_DATE_DAY]
						&& timeInfo[START_TIME_HOUR] == timeInfo[END_TIME_HOUR] - 1
						&& timeInfo[START_TIME_MINUTE] == timeInfo[END_TIME_MINUTE]) {
					timeInfo[END_TIME_HOUR] = timeInfo[START_TIME_HOUR];
				}
				isDeadline = true;
			}
		}

		if (isDeadline) {
			return Constant.DEADLINE_TASK_TYPE_INT;
		} else {
			if (timeInfo[END_TIME_HOUR] > 24) {
				timeInfo[END_TIME_HOUR] -= 24;
				Time newTime = Time.convertInfoToTime(dateInfo, timeInfo, 2);
				newTime = newTime.addDay(1);
				int[] newEndDate = getDateInfoFromTimeObject(newTime);
				Methods.transerIntArray(newEndDate, 0, dateInfo, 3, 3);
			}

			Time startTime = Time.convertInfoToTime(dateInfo, timeInfo, 1);
			Time endTime = Time.convertInfoToTime(dateInfo, timeInfo, 2);
			Long diff = (endTime.getTime() - startTime.getTime());
			if (diff < 0) {
				if (diff < -86400000) {
					String newFeedback = Constant.START_TIME_LATER_THAN_END_TIME;
					feedback[0] += newFeedback;
					return Constant.UNKNOWN_TASK_TYPE_INT;
				} else {
					endTime = endTime.addDay(1);
					int[] newEndDate = getDateInfoFromTimeObject(endTime);
					Methods.transerIntArray(newEndDate, 0, dateInfo, 3, 3);
				}
			}
			return Constant.TIME_TASK_TYPE_INT;
		}
	}

	private static boolean isDeadlineKeyword(String curTarget) {
		return curTarget.equals("by") || curTarget.equals("before")
				|| curTarget.equals("due") || curTarget.equals("deadline");
	}

	private static int[] adjustDate(int[] dateInfo) {
		if (dateInfo[START_DATE_YEAR] != NON_EXISTENT
				&& dateInfo[END_DATE_YEAR] == NON_EXISTENT) {
			Methods.transerIntArray(dateInfo, 0, dateInfo, 3, 3);
		}
		return dateInfo;
	}

	private static int[] adjustDateForSearch(int[] dateInfo,
			boolean[] searchOption) {
		if (dateInfo[START_DATE_YEAR] != NON_EXISTENT
				&& dateInfo[END_DATE_YEAR] == NON_EXISTENT) {
			if (searchOption[Constant.SEARCHOPTION_IS_BEFORE]) {

				Methods.transerIntArray(dateInfo, 0, dateInfo, 3, 3);

				Time newTime = Time.currentTime();
				int[] newDateInfo = getDateInfoFromTimeObject(newTime);
				Methods.transerIntArray(newDateInfo, 0, dateInfo, 0, 3);
			} else if (searchOption[Constant.SEARCHOPTION_IS_AFTER]) {
				Time newTime = new Time(dateInfo[START_DATE_YEAR],
						dateInfo[START_DATE_MONTH], dateInfo[START_DATE_DAY],
						0, 0);
				newTime = newTime.addDay(10000);
				int[] newDateInfo = getDateInfoFromTimeObject(newTime);
				Methods.transerIntArray(newDateInfo, 0, dateInfo, 3, 3);

			} else {
				Methods.transerIntArray(dateInfo, 0, dateInfo, 3, 3);
			}

			searchOption[Constant.SEARCHOPTION_DATE_ADJUSTED] = true;
		}
		return dateInfo;
	}

	private static int[] adjustTime(int[] timeInfo, boolean[] searchOption,
			int[] dateInfo) {
		int[] newTimeInfo = new int[6];

		if ((timeInfo[TIME_AMPM_MARK] == 0 && timeInfo[END_TIME_AMPM_MARK] == 1)) {
			if (timeInfo[END_TIME_HOUR_RAW] - timeInfo[START_TIME_HOUR] < 0) {
				if (timeInfo[START_TIME_HOUR] > 12) {
					timeInfo[START_TIME_HOUR] %= 12;
				}
				timeInfo[START_TIME_HOUR] += 12;
			} else if (timeInfo[END_TIME_HOUR_RAW] - timeInfo[START_TIME_HOUR] >= 12) {
				timeInfo[START_TIME_HOUR] += 12;
			}
		} else if (timeInfo[TIME_AMPM_MARK] == 1
				&& timeInfo[END_TIME_AMPM_MARK] == 0) {
			if (timeInfo[END_TIME_HOUR_RAW] - timeInfo[START_TIME_HOUR] < 0
					&& (timeInfo[END_TIME_HOUR_RAW] - timeInfo[START_TIME_HOUR] >= -12)) {
				timeInfo[END_TIME_HOUR_RAW] += 12;
			}
		}

		Methods.transerIntArray(timeInfo, 0, newTimeInfo, 0, 2);
		Methods.transerIntArray(timeInfo, 3, newTimeInfo, 2, 2);
		Methods.transerIntArray(timeInfo, 6, newTimeInfo, 4, 2);

		if (searchOption[Constant.SEARCHOPTION_IS_SEARCH] != true) {
			if (newTimeInfo[START_TIME_HOUR] != NON_EXISTENT
					&& newTimeInfo[2] == NON_EXISTENT) {
				newTimeInfo[2] = newTimeInfo[START_TIME_HOUR] + 1;
				newTimeInfo[3] = newTimeInfo[START_TIME_MINUTE];
			}
		} else {
			if (newTimeInfo[START_TIME_HOUR] != NON_EXISTENT
					&& newTimeInfo[END_TIME_HOUR] == NON_EXISTENT) {
				if (searchOption[Constant.SEARCHOPTION_DATE_ADJUSTED] == true) {
					Methods.transerIntArray(newTimeInfo, 0, newTimeInfo, 2, 2);
					newTimeInfo[START_TIME_HOUR] = 0;
					newTimeInfo[START_TIME_MINUTE] = 0;
				} else if (dateInfo[START_DATE_YEAR] == -1) {
					if (searchOption[Constant.SEARCHOPTION_IS_AFTER] != true) {
						Methods.transerIntArray(newTimeInfo, 0, newTimeInfo, 2,
								2);
						newTimeInfo[START_TIME_HOUR] = NON_EXISTENT;
						newTimeInfo[START_TIME_MINUTE] = NON_EXISTENT;
					}
				} else {
					Methods.transerIntArray(newTimeInfo, 0, newTimeInfo, 2, 2);
				}
			}
		}

		return newTimeInfo;
	}

	public static Time[] adjustDateAndTimeForSearch(int[] dateInfo,
			int[] timeInfo, String[] feedback, String keyword, String type,
			int undoneOption, boolean isAll) {
		Time[] finalTime = new Time[2];
		boolean bothNull = false;

		if (dateInfo[START_DATE_YEAR] == NON_EXISTENT
				&& dateInfo[END_DATE_YEAR] == NON_EXISTENT) {
			if (timeInfo[START_TIME_HOUR] == NON_EXISTENT
					&& timeInfo[END_TIME_HOUR] == NON_EXISTENT) {
				bothNull = true;
			} else if (timeInfo[START_TIME_HOUR] == NON_EXISTENT
					&& timeInfo[END_TIME_HOUR] != NON_EXISTENT) {
				timeInfo[START_TIME_HOUR] = 0;
				timeInfo[START_TIME_MINUTE] = 0;

				Time newTime = Time.currentTime();
				int[] newDateInfo = getDateInfoFromTimeObject(newTime);
				Methods.transerIntArray(newDateInfo, 0, dateInfo, 0, 3);

				int[] tempTimeInfo = new int[2];
				Methods.transerIntArray(timeInfo, 2, tempTimeInfo, 0, 2);

				int[] tempDateInfo = getNextAvailableDay(newTime, tempTimeInfo,
						1);
				Methods.transerIntArray(tempDateInfo, 0, dateInfo, 3, 3);

			} else if (timeInfo[START_TIME_HOUR] != NON_EXISTENT
					&& timeInfo[END_TIME_HOUR] == NON_EXISTENT) {
				timeInfo[END_TIME_HOUR] = 23;
				timeInfo[END_TIME_MINUTE] = 59;

				Time newTime = Time.currentTime();
				int[] tempDateInfo = getNextAvailableDay(newTime, timeInfo, 1);
				Methods.transerIntArray(tempDateInfo, 0, dateInfo, 0, 3);

				Time futureTime = new Time(dateInfo[START_DATE_YEAR],
						dateInfo[START_DATE_MONTH], dateInfo[START_DATE_DAY],
						0, 0);
				futureTime = futureTime.addDay(10000);
				int[] newDateInfo = getDateInfoFromTimeObject(futureTime);
				Methods.transerIntArray(newDateInfo, 0, dateInfo, 3, 3);

			} else {
				Time newTime = Time.currentTime();
				int[] tempDateInfo = getNextAvailableDay(newTime, timeInfo, 1);
				Methods.transerIntArray(tempDateInfo, 0, dateInfo, 0, 3);

				Time startTime = new Time(dateInfo[START_DATE_YEAR],
						dateInfo[START_DATE_MONTH], dateInfo[START_DATE_DAY],
						timeInfo[START_TIME_HOUR], timeInfo[START_TIME_MINUTE]);
				int[] tempTimeInfo = new int[2];
				Methods.transerIntArray(timeInfo, 2, tempTimeInfo, 0, 2);

				tempDateInfo = getNextAvailableDay(startTime, tempTimeInfo, 1);
				Methods.transerIntArray(tempDateInfo, 0, dateInfo, 3, 3);
			}
		} else {
			if (timeInfo[START_TIME_HOUR] == NON_EXISTENT
					&& timeInfo[END_TIME_HOUR] == NON_EXISTENT) {
				timeInfo[START_TIME_HOUR] = 0;
				timeInfo[START_TIME_MINUTE] = 0;
				timeInfo[END_TIME_HOUR] = 23;
				timeInfo[END_TIME_MINUTE] = 59;
			}
		}

		if (!bothNull) {
			Time startTime = new Time(dateInfo[START_DATE_YEAR],
					dateInfo[START_DATE_MONTH], dateInfo[START_DATE_DAY],
					timeInfo[START_TIME_HOUR], timeInfo[START_TIME_MINUTE]);
			Time endTime = new Time(dateInfo[END_DATE_YEAR],
					dateInfo[END_DATE_MONTH], dateInfo[END_DATE_DAY],
					timeInfo[END_TIME_HOUR], timeInfo[END_TIME_MINUTE]);
			Long diff = (endTime.getTime() - startTime.getTime()) / 100000;
			if (diff < 0) {
				if (diff < -316224) {
					String newFeedback = Constant.START_TIME_LATER_THAN_END_TIME;
					feedback[0] += newFeedback;

				} else if (diff < -6048) {
					startTime = new Time(dateInfo[START_DATE_YEAR] - 1,
							dateInfo[START_DATE_MONTH],
							dateInfo[START_DATE_DAY],
							timeInfo[START_TIME_HOUR],
							timeInfo[START_TIME_MINUTE]);
				} else if (diff < -864) {
					startTime = new Time(dateInfo[START_DATE_YEAR],
							dateInfo[START_DATE_MONTH],
							dateInfo[START_DATE_DAY] - 7,
							timeInfo[START_TIME_HOUR],
							timeInfo[START_TIME_MINUTE]);
				} else {
					startTime = new Time(dateInfo[START_DATE_YEAR],
							dateInfo[START_DATE_MONTH],
							dateInfo[START_DATE_DAY] - 1,
							timeInfo[START_TIME_HOUR],
							timeInfo[START_TIME_MINUTE]);
				}
			}

			finalTime[0] = startTime;
			finalTime[1] = endTime;
		} else {
			if (keyword.equals("") && type == null && undoneOption == 0
					&& !isAll) {
				Time now = Time.currentTime();
				int[] today = getDateInfoFromTimeObject(now);

				finalTime[0] = new Time(today[0], today[1], today[2], 0, 0);
				finalTime[1] = new Time(today[0], today[1], today[2], 23, 59);
			} else {
				finalTime[0] = null;
				finalTime[1] = null;
			}
		}
		return finalTime;
	}

	private static void addOneTimeInfo(int[] curInfo, int[] timeInfo,
			String[] feedback, int index) {
		if (timeInfo[START_TIME_HOUR] == NON_EXISTENT) {
			Methods.transerIntArray(curInfo, 0, timeInfo, 0, 3);
			timeInfo[START_TIME_POSITION] = index;
		} else if (timeInfo[END_TIME_HOUR_RAW] == NON_EXISTENT) {
			Methods.transerIntArray(curInfo, 0, timeInfo, 3, 3);
			timeInfo[END_TIME_POSITION] = index;
		}
	}

	private static void addOneDateInfo(int[] curInfo, int[] dateInfo,
			String[] feedback, int index) {
		if (dateInfo[START_DATE_YEAR] == NON_EXISTENT) {
			Methods.transerIntArray(curInfo, 0, dateInfo, 0, 3);
			dateInfo[START_DATE_POSITION] = index;
		} else if (dateInfo[END_DATE_YEAR] == NON_EXISTENT) {
			Methods.transerIntArray(curInfo, 0, dateInfo, 3, 3);
			dateInfo[END_DATE_POSITION] = index;
		}
	}

	private static void initializeInfoArray(int[] infoArray) {
		for (int i = 0; i < infoArray.length; i++) {
			infoArray[i] = NON_EXISTENT;
		}
	}

	private static int[] getNextAvailableDay(Time currentTime,
			int[] targetTimeInfo, int Offsetunit) {
		// assert timeInfo != null
		int numOfoffset = getNumOfOffset(currentTime, targetTimeInfo);
		int offset = numOfoffset * Offsetunit;
		Time newTime = currentTime.addDay(offset);
		int[] dateInfo = getDateInfoFromTimeObject(newTime);

		return dateInfo;
	}

	private static int getNumOfOffset(Time currentTime, int[] targetTimeInfo) {
		int currentHour = currentTime.getHours();
		int currentMinute = currentTime.getMinutes();
		// assert targetTimeInfo != null
		int targetHour = targetTimeInfo[HOUR];
		int targetMinute = targetTimeInfo[MINUTE];

		int numOfoffset = ZERO;
		if (compareTime(currentHour, currentMinute, targetHour, targetMinute) == TARGET_IS_EARLIER) {
			numOfoffset = ONE;
		} else {
			numOfoffset = ZERO;
		}

		return numOfoffset;
	}

	private static int[] analyzeTime(String content, boolean isPeriod) {
		int[] time = null;

		time = analyzeTimeInAMPMformat(content);
		if (time == null) {
			time = analyzeTimeWithoutAMPM(content, isPeriod);
		}

		return time;
	}

	private static int[] analyzeTimeWithoutAMPM(String content,
			boolean isAMPMMode) {
		String[] splitedTime = content.trim().split(":");
		int[] timeInfo = null;
		int hours = NON_EXISTENT;
		int minutes = 0;

		// assert length >= 1
		if (splitedTime.length < 2) {
			if (!isAMPMMode) {
				return null;
			}
			splitedTime = analyseTimeWithoutColon(splitedTime);
			hours = Methods.safeParseInt(splitedTime[HOUR]);
			minutes = Methods.safeParseInt(splitedTime[MINUTE]);
		} else {
			hours = Methods.safeParseInt(splitedTime[HOUR]);
			minutes = Methods.safeParseInt(splitedTime[MINUTE]);
		}

		if (hours != NON_EXISTENT && hours <= 24 && minutes != NON_EXISTENT
				&& minutes <= 60) {
			timeInfo = new int[3];
			timeInfo[HOUR] = hours;
			timeInfo[MINUTE] = minutes;
			timeInfo[TIME_AMPM_MARK] = 0;
		}

		return timeInfo;
	}

	private static String[] analyseTimeWithoutColon(String[] splitedTime) {
		String[] newSplitedTime = new String[TIME_INFO_LENGTH];
		int hours = -1;
		int minutes = -1;

		int time = Methods.safeParseInt(splitedTime[HOUR]);
		if (time != -1) {
			if (time < 100) {
				hours = time;
				minutes = 0;
			} else {
				hours = time / 100;
				minutes = time % 100;
			}
		}

		newSplitedTime[HOUR] = hours + Constant.EMPTY_STRING;
		newSplitedTime[MINUTE] = minutes + Constant.EMPTY_STRING;
		return newSplitedTime;
	}

	private static int[] analyzeTimeInAMPMformat(String content) {
		int indexOfAM = getIndexOfAM(content);
		int indexOfPM = getIndexOfPM(content);
		String timeContent;
		int[] time = null;

		if (indexOfAM != NON_EXISTENT) {
			timeContent = content.substring(START_OF_ARRAY, indexOfAM);
			time = analyzeTimeWithoutAMPM(timeContent, true);
			if (time != null) {
				time = adjustTimeInAM(time);
			}
		} else if (indexOfPM != NON_EXISTENT) {
			timeContent = content.substring(START_OF_ARRAY, indexOfPM);
			time = analyzeTimeWithoutAMPM(timeContent, true);
			if (time != null) {
				time = adjustTimeInPM(time);
			}
		} else {
			return null;
		}

		return time;
	}

	private static int[] adjustTimeInPM(int[] time) {
		int[] newTime = new int[3];

		int hours = time[HOUR];
		int minutes = time[MINUTE];
		if (hours == 12 && minutes == 0) {
			return time;
		} else {
			if (hours >= 12) {
				hours %= 12;
			}
			hours += 12;
		}

		newTime[HOUR] = hours;
		newTime[MINUTE] = minutes;
		newTime[TIME_AMPM_MARK] = 1;
		return newTime;
	}

	private static int[] adjustTimeInAM(int[] time) {
		int[] newTime = new int[3];

		int hours = time[HOUR];
		int minutes = time[MINUTE];
		if (hours == 12 && minutes == 0) {
			hours = 0;
		} else {
			if (hours >= 12) {
				hours %= 12;
			}
		}

		newTime[HOUR] = hours;
		newTime[MINUTE] = minutes;
		newTime[TIME_AMPM_MARK] = 1;
		return newTime;
	}

	private static int getIndexOfPM(String content) {
		return Math.max(content.indexOf("pm"), content.indexOf("PM"));
	}

	private static int getIndexOfAM(String content) {
		return Math.max(content.indexOf("am"), content.indexOf("AM"));
	}

	private static int[] analyzeDate(String content) {
		int[] date = null;

		date = analyzeDateInStandardformat(content);
		if (date == null) {
			date = analyzeDateInCasualFormat(content);
		}

		return date;
	}

	private static int[] analyzeDateInCasualFormat(String content) {
		int[] date = null;
		String contentInLowerCase = content.toLowerCase();

		int[] dayType = determineDayType(contentInLowerCase);
		date = getDayByCasualDayType(dayType);

		return date;

	}

	private static int[] getDayByCasualDayType(int[] dayType) {
		if (dayType == null) {
			return null;
		}

		int[] dateInfo = null;
		Time currentTime = Time.currentTime();
		int currentMonToSun = getMonToSun(currentTime);
		int daysToOffset = 0;

		if (dayType[CASUAL_TYPE] == MON_TO_SUN) {
			int targetMonToSun = dayType[DETAIL];
			daysToOffset = (targetMonToSun - currentMonToSun);
			if (daysToOffset <= 0) {
				daysToOffset += PERIOD_OF_A_WEEK;
			}

		} else if (dayType[CASUAL_TYPE] == OFFSET) {
			daysToOffset = dayType[DETAIL];
		} else {
			return null;
		}

		Time newTime = currentTime.addDay(daysToOffset);
		dateInfo = getDateInfoFromTimeObject(newTime);

		return dateInfo;
	}

	private static int[] getDateInfoFromTimeObject(Time newTime) {
		int years = newTime.getYear();
		int months = newTime.getMonth();
		int days = newTime.getDate();

		int[] dateInfo = new int[3];
		dateInfo[YEAR_IN_LONG_FORMAT] = years;
		dateInfo[MONTH_IN_LONG_FORMAT] = months + 1;
		dateInfo[DAY_IN_LONG_FORMAT] = days;

		return dateInfo;
	}

	private static int compareTime(int currentHour, int currentMinute,
			int targetHour, int targetMinute) {
		if (currentHour > targetHour) {
			return TARGET_IS_EARLIER;
		} else if (currentHour == targetHour) {
			if (currentMinute > targetMinute) {
				return TARGET_IS_EARLIER;
			} else if (currentMinute == targetMinute) {
				return BOTH_ARE_THE_SAME;
			} else {
				return TARGET_IS_LATER;
			}
		} else {
			return TARGET_IS_LATER;
		}
	}

	// will be implemented in Time Class later
	private static int getMonToSun(Time currentTime) {
		return currentTime.getMonToSun();
	}

	private static int[] determineDayType(String content) {
		int[] dayTypeinfo = new int[2];
		if (content == null) {
			return null;
		}

		if (content.equals("mon") || content.equals("monday")) {
			dayTypeinfo[CASUAL_TYPE] = MON_TO_SUN;
			dayTypeinfo[DETAIL] = MON;
		} else if (content.equals("tue") || content.equals("tues")
				|| content.equals("tuesday")) {
			dayTypeinfo[CASUAL_TYPE] = MON_TO_SUN;
			dayTypeinfo[DETAIL] = TUE;
		} else if (content.equals("wed") || content.equals("wedn")
				|| content.equals("weds") || content.equals("wedns")
				|| content.equals("wednsday")) {
			dayTypeinfo[CASUAL_TYPE] = MON_TO_SUN;
			dayTypeinfo[DETAIL] = WEDN;
		} else if (content.equals("thur") || content.equals("thurs")
				|| content.equals("thursday")) {
			dayTypeinfo[CASUAL_TYPE] = MON_TO_SUN;
			dayTypeinfo[DETAIL] = THUR;
		} else if (content.equals("fri") || content.equals("friday")) {
			dayTypeinfo[CASUAL_TYPE] = MON_TO_SUN;
			dayTypeinfo[DETAIL] = FRI;
		} else if (content.equals("sat") || content.equals("saturday")) {
			dayTypeinfo[CASUAL_TYPE] = MON_TO_SUN;
			dayTypeinfo[DETAIL] = SAT;
		} else if (content.equals("sun") || content.equals("sunday")) {
			dayTypeinfo[CASUAL_TYPE] = MON_TO_SUN;
			dayTypeinfo[DETAIL] = SUN;
		} else if (content.equals("yesterday") || content.equals("ytd")) {
			dayTypeinfo[CASUAL_TYPE] = OFFSET;
			dayTypeinfo[DETAIL] = YESTERDAY;
		} else if (content.equals("today")) {
			dayTypeinfo[CASUAL_TYPE] = OFFSET;
			dayTypeinfo[DETAIL] = TODAY;
		} else if (content.equals("tomorrow") || content.equals("tmr")) {
			dayTypeinfo[CASUAL_TYPE] = OFFSET;
			dayTypeinfo[DETAIL] = TOMORROW;
		} else {
			return null;
		}

		return dayTypeinfo;
	}

	// this method doesn't check whether a date is valid
	// for example, "2014/3/40" will be taken as 2014 Mar 40th
	// this error will be handle when construct the Time Object
	private static int[] analyzeDateInStandardformat(String content) {
		String[] splitedDate = content.trim().split("/");
		int[] dateInfo = null;

		int years = NON_EXISTENT;
		int months = NON_EXISTENT;
		int days = NON_EXISTENT;

		if (splitedDate.length == 2) {
			months = Methods
					.safeParseInt(splitedDate[MONTH_IN_SHORT_FORMAT_SINGAPORE]);
			days = Methods
					.safeParseInt(splitedDate[DAY_IN_SHORT_FORMAT_SINGAPORE]);
		} else if (splitedDate.length > 2) {
			years = Methods
					.safeParseInt(splitedDate[YEAR_IN_LONG_FORMAT_SINGAPORE]);
			if (years > 0 && years < 1000) {
				years += 2000;
			}
			months = Methods
					.safeParseInt(splitedDate[MONTH_IN_LONG_FORMAT_SINGAPORE]);
			days = Methods
					.safeParseInt(splitedDate[DAY_IN_LONG_FORMAT_SINGAPORE]);
		}

		if (months != NON_EXISTENT && days != NON_EXISTENT) {
			if (years == NON_EXISTENT) {
				years = getNextAvailableYear(months, days);
			}
			if (years < 5000 && months < 100 && days < 100) {
				dateInfo = new int[DATE_INFO_LENGTH];
				dateInfo[YEAR_IN_LONG_FORMAT] = years;
				dateInfo[MONTH_IN_LONG_FORMAT] = months;
				dateInfo[DAY_IN_LONG_FORMAT] = days;
			}
		}

		return dateInfo;
	}

	private static int getNextAvailableYear(int months, int days) {
		Time currentTime = Time.currentTime();
		int currentYears = currentTime.getYear();
		int currentMonths = currentTime.getMonth() + 1;
		int currentDays = currentTime.getDate();

		int years = currentYears;

		if (currentMonths < months) {
			// do nothing
		} else if (currentMonths == months) {
			if (currentDays >= days) {
				years += 1;
			}
		} else {
			years += 1;
		}
		return years;
	}
}
